const { reject } = require("lodash")

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) { // 立即执行
    try { // 捕获执行器一些错误
      executor(this.resolve, this.reject)
    } catch(e) {
      this.reject(e)
    }
  }
  // 状态
  status = PENDING
  // 成功之后的值
  value = undefined
  // 失败的原因
  reason = undefined
  // 成功回调
  successCallBack = []
  // 失败回调
  failCallBack = []

  resolve = (value) => {
    // 状态一旦改变就不可更改
    if(this.status !== PENDING) return
    // 状态更新成成功
    this.status = FULFILLED
    // 记录成功后的值
    this.value = value
    // 等待成功回调存在时，执行异步代码
    // 同一个promise实例方法，then的方法可以多次被调用的，因此需要将回调函数存储起来依次执行
    // 当执行完一个之后需要删除当前执行的一个回调，直到全部执行完成
    
    while (this.successCallBack.length) {
      this.successCallBack.shift()()
    }

    // this.successCallBack && this.successCallBack(this.value)
  }

  reject = (reason) => {
    // 状态一旦改变就不可更改
    if(this.status !== PENDING) return
    // 状态改成失败
    this.status = REJECTED
    // 记录失败的原因
    this.reason = reason
    // 等待成功回调存在时，执行异步代码
    // this.failCallBack && this.failCallBack(this.reason)
    
    while (this.failCallBack.length) {
      this.failCallBack.shift()()
    }
  }

  then(successCallBack, failCallBack) {
    successCallBack = successCallBack ? successCallBack : value => value
    failCallBack = failCallBack ? failCallBack : reason => reason
    let promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 使用settimeout是为了将里面的代码编程异步代码，那么promise就能获取到了
        setTimeout(() => {
          try {
            let x = successCallBack(this.value)
            // x 判断是普通纸还是promise对象
            // 普通值：直接resolve
            // promise对象：查看promise返回结果，然后决定是resolve还是reject
            // resolve(x)
            resolvePromise(promise, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else if (this.status === REJECTED) {
        // 使用settimeout是为了将里面的代码编程异步代码，那么promise就能获取到了
        setTimeout(() => {
          try {
            let x = failCallBack(this.reason)
            // x 判断是普通纸还是promise对象
            // 普通值：直接resolve
            // promise对象：查看promise返回结果，然后决定是resolve还是reject
            // resolve(x)
            resolvePromise(promise, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else {
        // 异步情况
        // 等待
        // 因为不知道此时该执行成功还是失败回调，于是先将其存储起来
        // 当异步代码执行完成后，如果是成功，则执行resolve，否则执行reject
        // 同一个promise实例方法，then的方法可以多次被调用的，因此需要将回调函数存储起来依次执行

        
        this.successCallBack.push(() => {
          // 使用settimeout是为了将里面的代码编程异步代码，那么promise就能获取到了
          setTimeout(() => {
            try {
              let x = successCallBack(this.value)
              // x 判断是普通纸还是promise对象
              // 普通值：直接resolve
              // promise对象：查看promise返回结果，然后决定是resolve还是reject
              // resolve(x)
              resolvePromise(promise, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0);
        })
        this.failCallBack.push(() => {
          // 使用settimeout是为了将里面的代码编程异步代码，那么promise就能获取到了
          setTimeout(() => {
            try {
              let x = failCallBack(this.reason)
              // x 判断是普通纸还是promise对象
              // 普通值：直接resolve
              // promise对象：查看promise返回结果，然后决定是resolve还是reject
              // resolve(x)
              resolvePromise(promise, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0);
        })
      }
    })
    return promise
  }

  catch(failCallBack) {
    return this.then(undefined, failCallBack)
  }

  finally (callBack) {
    return this.then(value => {
      return MyPromise.resolve(callBack()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callBack()).then(() => {throw reason})
    })
  }

  static all (array) {
    let result = []
    let index = 0
    
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value
        index++
        if (index === array.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < array.length; i++) {
        const current = array[i];
        if (current instanceof MyPromise) { // 如果是Mypromise对象
          // 没有等待异步操作，因此需要处理异步操作
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          // 普通值
          addData(i, current)
        }
      }
    })
  }

  static resolve (value) {
    if (value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }

  static race (array) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < array.length; i++) {
        MyPromise.resolve(array[i]).then(data => {
          resolve(data)
        }, (reason) => reject(reason))
      }
    })
  }
}

function resolvePromise(promise, x, resolve, reject) {
  // 避免自己调用自己，导致被循环调用的情况
  if (promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  
  if (x instanceof MyPromise) {
    // promise对象：查看promise返回结果，然后决定是resolve还是reject
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}